/*
 * @file bench_base.h
 *
 * Basic facilities to support benchmarking
 *
 * @author Dahua Lin
 */

#ifdef _MSC_VER
#pragma once
#endif

#ifndef GML_BENCH_BASE_H
#define GML_BENCH_BASE_H

#include <gmlpp/base/basic_defs.h>
#include <gmlpp/base/timer.h>
#include <cstdio>

namespace gml
{

	/**
	 * The base class for benchmarking task
	 */
	template<class Derived>
	class BenchTask
	{
	public:
		GML_ENSURE_INLINE
		Derived& derived()
		{
			return static_cast<Derived&>(*this);
		}

		GML_ENSURE_INLINE
		void run()
		{
			derived().run();
		}

	};

	struct BenchStats
	{
		double elapsed_seconds;
		size_t num_times;

		double secs_per_time() const
		{
			return elapsed_seconds / double(num_times);
		}

		double msecs_per_time() const
		{
			return (elapsed_seconds * 1.0e3) / double(num_times);
		}

		double usecs_per_time() const
		{
			return (elapsed_seconds * 1.0e6) / double(num_times);
		}

		double nsecs_per_time() const
		{
			return (elapsed_seconds * 1.0e9) / double(num_times);
		}

		double rate() const
		{
			return double(num_times) / double(elapsed_seconds);
		}

		double rate_K() const
		{
			return double(num_times) * 1.0e-3 / double(elapsed_seconds);
		}

		double rate_M() const
		{
			return double(num_times) * 1.0e-6 / double(elapsed_seconds);
		}

		double rate_G() const
		{
			return double(num_times) * 1.0e-9 / double(elapsed_seconds);
		}
	};



	template<class Derived>
	inline BenchStats bench_task(BenchTask<Derived>& task, size_t ntimes, size_t warm_times = 0)
	{
		for (size_t i = 0; i < warm_times; ++i)
		{
			task.run();
		}

		Timer tm(true);
		for (size_t i = 0; i < ntimes; ++i)
		{
			task.run();
		}
		double et = tm.elapsed_secs();

		BenchStats s;
		s.elapsed_seconds = et;
		s.num_times = ntimes;
		return s;
	}

}


#endif

